﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using HQPad.Helpers;
using HQPad.Services;

namespace HQPad.Models
{
    /// <summary>
    /// Model for data related to the character header.
    /// Stats, race, class and guilds.
    /// </summary>
    public class CharacterHeader
    {
        public int ID { get; set; }
        public int Rank { get; set; }
        public int Points { get; set; }
        public int EffectivePoints { get; set; }
        public int Status { get; set; }
        public int UnspentPoints { get; set; }
        public int UnspentStatus { get; set; }
        public PointsTable Table { get; set; }
        public SelectList PrimaryGuilds { get; set; }
        public SelectList SecondaryGuilds { get; set; }
        public SelectList TertiaryGuilds { get; set; }

        public SelectList Races { get; set; }
        public SelectList Classes { get; set; }
    }

    /// <summary>
    /// Model item for the adventure record.
    /// </summary>
    public class RecordItem
    {
        public DateTime Date { get; set; }
        public string Description { get; set; }
        public int Points { get; set; }
        public int ID { get; set; }
    }

    /// <summary>
    /// Model item for the character skill list.
    /// </summary>
    public class SkillItem : IEquatable<SkillItem>
    {
        public string Name { get; set; }
        public int Ranks { get; set; }
        public int Cost { get; set; }
        public string CostDescription { get; set; }
        public int Status { get; set; }
        public int? MaxRanks { get; set; }
        public bool StatusSkill { get; set; }
        public string Slug { get; set; }
        public string Group { get; set; }
        public string Description { get; set; }
        public bool Allowed { get; set; }

        private NameValueCollection values = null;

        public NameValueCollection Values
        {
            get { return values; }
        }

        public void AddValue(string name, string value)
        {
            if (values == null)
                values = new NameValueCollection();

            values.Add(name, value);
        }

        public bool Equals(SkillItem toEquate)
        {
            return (
                this.Slug == toEquate.Slug &&
                this.Ranks == toEquate.Ranks &&
                this.MaxRanks == toEquate.MaxRanks);
        }
    }

    public class SkillItemChange
    {
        public SkillItem Item { get; set; }
        public bool Removed { get; set; }
        public bool Added { get; set; }
        public bool Updated { get; set; }
    }

    /*
     *  Model classes to help returning stateful json results.
     */
    public enum JsonStatus
    {
        Ok,
        Error
    }

    public class JsonResponse
    {
        public JsonStatus Status { get; set; }
        public string Message { get; set; }
        public object Data { get; set; }
    }

    /*
     * Model classes for the character silhouette control.
     */
    public class SilhouetteRecord
    {
        public LifeRecord Life { get; set; }
        public List<ACRecord> AC { get; set; }

        public SilhouetteRecord()
        {
            AC = new List<ACRecord>();
        }
    }

    /// <summary>
    /// Total and locational life.
    /// </summary>
    public class LifeRecord
    {
        public int TotalLife { get; set; }
        public int LocationalLife { get; set; }
    }

    public class ACRecord
    {
        public string Name { get; set; }
        public int Total { get; set; }
        public int Pure { get; set; }
        public List<ACSource> Sources { get; set; }

        public ACRecord()
        {
            Sources = new List<ACSource>();
        }
    }

    public class ACSource
    {
        public string Name { get; set; }
        public string Parent { get; set; }
        public int Value { get; set; }
    }


    /*
     *  Model classes for the magic/schools page. 
     */
    #region Magic
    public class SchoolAccessModel
    {
        private List<SchoolAccessModelItem> items;
        public List<SchoolAccessModelItem> AccessItems
        {
            get { return items; }
        }

        private List<SchoolAccessRecord> records;
        public List<SchoolAccessRecord> Records
        {
            get { return records; }
        }

        /// <summary>Reference list of all schools of magic.</summary>
        public List<SchoolItem> Schools { get; set; }

        public List<LearntSpellsAllotment> Status { get; set; }

        public SchoolAccessModel()
        {
            items = new List<SchoolAccessModelItem>();
            records = new List<SchoolAccessRecord>();
        }

        public string StatusToJson()
        {
            StringBuilder json = new StringBuilder("{");
            bool first = true;

            foreach (var status in Status)
            {
                if (!first)
                    json.Append(",");
                else
                    first = false;

                json.AppendFormat("{0}: {1}", Enum.GetName(typeof(SchoolStatusAssignmentType), status.Level).ToLowerInvariant(), status.AvailableToLearn);
            }

            json.Append("}");

            return json.ToString();
        }
    }

    public class SchoolAccessModelItem
    {
        private string selectedSchoolSlug = string.Empty;

        /// <summary>Slug of the school of magic guild</summary>
        public string SchoolSlug {
            get { return selectedSchoolSlug; }
            set { selectedSchoolSlug = value; }
        }

        /// <summary>Mage, Wizard or Sorceror</summary>
        public string AccessType { get; set; }

        /// <summary>Display name of the source (skill etc.)</summary>
        public string AccessName { get; set; }

        /// <summary>Slug of the source (skill etc.). Can be empty if a bonus?</summary>
        public string AccessSlug { get; set; }

    }

    /// <summary>A school of magic item used to populate lists.</summary>
    public class SchoolItem
    {
        public string Name { get; set; }
        public string Slug { get; set; }
    }

    /// <summary>Level of access to a school of magic.</summary>
    public class SchoolAccessRecord
    {
        public string SourceSlug { get; set; }
        public string SchoolName { get; set; }
        public string Access { get; set; }
        public bool PrimaryAllowed { get; set; }
        public bool SpecialistAllowed { get; set; }
    }

    public class SchoolStatus
    {
        /// <summary>Schools where status has been spent or can be spent.</summary>
        private List<SchoolStatusAssignment> statuses;
        public List<SchoolStatusAssignment> Status
        {
            get { return statuses; }
        }
        
        /// <summary>Total status available by type.</summary>
        private List<SchoolStatusTotal> totals;
        public List<SchoolStatusTotal> Totals
        {
            get { return totals; }
        }

        public SchoolStatus()
        {
            statuses = new List<SchoolStatusAssignment>();
            totals = new List<SchoolStatusTotal>();
        }

        public IEnumerable<SchoolStatusAssignmentGroup> GetGroupedStatusAssignments()
        {
            return
                (from s in Status
                 group s by new { s.SchoolSlug } into statusGroup
                 select new SchoolStatusAssignmentGroup
                 {
                     SchoolSlug = statusGroup.Key.SchoolSlug,
                     SchoolName = statusGroup.First().SchoolName,
                     Items = statusGroup.Select(a => new SchoolStatusAssignmentGroupItem  { Status = a.Status, Type = a.Type }).ToList()
                 });
        }

        /// <summary>
        /// Guild a json string for all the school/known spells available 
        /// to aid client site javascript validation of spell choices.
        /// </summary>
        /// <returns></returns>
        public string BuildJsonSchoolStatusArray()
        {
            StringBuilder json = new StringBuilder();

            foreach (SchoolStatusAssignment assignment in Status)
            {
                if (json.Length == 0)
                    json.Append("{");
                else
                    json.Append(",");

                json.AppendFormat(" '{0}_{2}' : {1}", assignment.Colour.ToLower(), assignment.SpellsAvailable, Enum.GetName(typeof(SchoolStatusAssignmentType), assignment.Type).ToLowerInvariant());
            }

            if (json.Length > 0)
            {
                json.Append("}");
                return json.ToString();
            }
            else
                return "null";
        }
    }

    /// <summary>Grouped spell assignment type.</summary>
    public class SchoolStatusAssignmentGroup
    {
        public string SchoolName { get; set; }
        public string SchoolSlug { get; set; }
        public List<SchoolStatusAssignmentGroupItem> Items { get; set; }
    }

    /// <summary>Grouped spell assignment item type.</summary>
    public class SchoolStatusAssignmentGroupItem
    {
        public SchoolStatusAssignmentType Type { get; set; }
        public int Status { get; set; }
    }


    /// <summary>Assignment of status to a school of magic.</summary>
    public class SchoolStatusAssignment
    {
        public SchoolStatusAssignmentType Type { get; set; }
        public string Colour { get; set; }
        public string SchoolName { get; set; }
        public string SchoolSlug { get; set; }
        public int Status { get; set; }
        public int SpellsAvailable { get; set; }
    }

    public class SchoolStatusTotal
    {
        public SchoolStatusAssignmentType Type { get; set; }
        public int Total { get; set; }
        public int SpellsAvailable { get; set; }
    }

    /// <summary>
    /// The model used for the list of available spells
    /// </summary>
    public class SpellListModel
    {
        List<SpellItem> spells;

        public List<SpellItem> Spells
        {
            get { return spells; }
        }

        public SpellListModel()
        {
            spells = new List<SpellItem>();
        }
    }

    /// <summary>A spell entry in the spells list.</summary>
    public class SpellItem
    {
        public string Colour { get; set; }
        public string Name { get; set; }
        public string Level { get; set; } // can be 1-5.
        public string Group { get; set; }
        public string GroupSlug { get; set; }
        public string Family { get; set; }
        public bool Selected { get; set; }
        public bool Attuned { get; set; }
        public bool SystemSpell { get; set; }
        public SchoolStatusAssignmentType AssignmentType { get; set; }

        public SpellItem()
        {
            Selected = false;
            Attuned = false;
            AssignmentType = SchoolStatusAssignmentType.Level1to8;
        }
    }
    #endregion

    #region Power
    public class SectAccessModel
    {
        private List<SectAccessModelItem> items;
        public List<SectAccessModelItem> AccessItems
        {
            get { return items; }
        }

        private List<SectAccessRecord> records;
        public List<SectAccessRecord> Records
        {
            get { return records; }
        }

        /// <summary>Reference list of all schools of magic.</summary>
        public List<SectItem> Sects { get; set; }

        public SectStatus Status { get; set; }

        public SectAccessModel()
        {
            items = new List<SectAccessModelItem>();
            records = new List<SectAccessRecord>();
        }

    }

    public class SectAccessModelItem
    {
        private string selectedSectSlug = string.Empty;

        /// <summary>Slug of the guild sect</summary>
        public string SectSlug
        {
            get { return selectedSectSlug; }
            set { selectedSectSlug = value; }
        }

        /// <summary>Lay, Ritual or Cosmic</summary>
        public string AccessType { get; set; }

        /// <summary>Display name of the source (skill etc.)</summary>
        public string AccessName { get; set; }

        /// <summary>Slug of the source (skill etc.). Can be empty if a bonus?</summary>
        public string AccessSlug { get; set; }

    }

    /// <summary>A sect guild used to populate lists.</summary>
    public class SectItem
    {
        public string Name { get; set; }
        public string Slug { get; set; }
    }

    /// <summary>Level of access to a sect.</summary>
    public class SectAccessRecord
    {
        public string SourceSlug { get; set; }
        public string SectName { get; set; }
        public string Access { get; set; }
    }

    public class SectStatus
    {
        /// <summary>Sects where status has been spent or can be spent.</summary>
        private List<SectStatusAssignment> statuses;
        public List<SectStatusAssignment> Status
        {
            get { return statuses; }
        }

        /// <summary>Total status available by type.</summary>
        private List<SectStatusTotal> totals;
        public List<SectStatusTotal> Totals
        {
            get { return totals; }
        }

        public SectStatus()
        {
            statuses = new List<SectStatusAssignment>();
            totals = new List<SectStatusTotal>();
        }

        public IEnumerable<SectStatusAssignmentGroup> GetGroupedStatusAssignments()
        {
            return
                (from s in Status
                 group s by new { s.SectSlug } into statusGroup
                 select new SectStatusAssignmentGroup
                 {
                     SectSlug = statusGroup.Key.SectSlug,
                     SectName = statusGroup.First().SectName,
                     Items = statusGroup.Select(a => new SectStatusAssignmentGroupItem { Status = a.Status, Type = a.Type }).ToList()
                 });
        }

        /// <summary>
        /// Guild a json string for all the sect/known invocations available 
        /// to aid client site javascript validation of invocation choices.
        /// </summary>
        /// <returns></returns>
        public string BuildJsonSectStatusArray()
        {
            StringBuilder json = new StringBuilder();

            foreach (SectStatusAssignment assignment in Status)
            {
                if (json.Length == 0)
                    json.Append("{");
                else
                    json.Append(",");

                json.AppendFormat(
                    " '{0}_{2}' : {1}", 
                    assignment.SectSlug.ToLower().Replace("-", "_"), 
                    assignment.InvocationsAvailable, 
                    Enum.GetName(typeof(SectStatusAssignmentType), assignment.Type).ToLowerInvariant());
            }

            if (json.Length > 0)
            {
                json.Append("}");
                return json.ToString();
            }
            else
                return "null";
        }
    }

    /// <summary>Grouped invocation assignment type.</summary>
    public class SectStatusAssignmentGroup
    {
        public string SectName { get; set; }
        public string SectSlug { get; set; }
        public List<SectStatusAssignmentGroupItem> Items { get; set; }
    }

    /// <summary>Grouped inocation assignment item type.</summary>
    public class SectStatusAssignmentGroupItem
    {
        public SectStatusAssignmentType Type { get; set; }
        public int Status { get; set; }
    }


    /// <summary>Assignment of status to a sect.</summary>
    public class SectStatusAssignment
    {
        public SectStatusAssignmentType Type { get; set; }
        public string SectName { get; set; }
        public string SectSlug { get; set; }
        public int Status { get; set; }
        public int InvocationsAvailable { get; set; }
    }

    public class SectStatusTotal
    {
        public SectStatusAssignmentType Type { get; set; }
        public int Total { get; set; }
        public int InvocationsAvailable { get; set; }
        public int InvocationsLearnt { get; set; }
    }

    /// <summary>
    /// The model used for the list of available invocations
    /// </summary>
    public class InvocationListModel
    {
        List<InvocationItem> invocations;

        public List<InvocationItem> Invocations
        {
            get { return invocations; }
        }

        public InvocationListModel()
        {
            invocations = new List<InvocationItem>();
        }
    }

    /// <summary>An invocation entry in the invocations list.</summary>
    public class InvocationItem
    {
        public string SectName { get; set; }
        public string SectSlug { get; set; }
        public string Sphere { get; set; }
        public string Name { get; set; }
        public string Rank { get; set; } // can be 1-5.
        public string Group { get; set; }
        public string GroupSlug { get; set; }
        public string Family { get; set; }
        public bool Selected { get; set; }
        public SectStatusAssignmentType AssignmentType { get; set; }

        public InvocationItem()
        {
            Selected = false;
            AssignmentType = SectStatusAssignmentType.Level1to8;
        }
    }
    #endregion

    /*
     *  Equipment, items and abilites
     */

    /// <summary>
    /// Contract for objects (models) that can have qualities.
    /// </summary>
    public interface IQualityContainer
    {
        int Id { get; }
        string ItemName { get; }
        List<QualityItem> Qualities { get; }
    }

    /// <summary>
    /// A piece of armour for a character.
    /// </summary>
    public class ArmourItem : IQualityContainer
    {
        public int Id { get; set; }
        public string ItemName { get; set; }
        public ArmourType Type { get; set; }
        public int Superior { get; set; }
        public int Empowered { get; set; }
        public int Ensorcelled { get; set; }
        public bool Equipped { get; set; }
        public string HQId { get; set; }

        protected List<QualityItem> qualities = null;

        public List<QualityItem> Qualities
        {
            get { return qualities; }
        }

        public ArmourItem()
        {
            qualities = new List<QualityItem>();
        }

        public string Description
        {
            get
            {
                String desc = string.Empty;

                switch (Type)
                {
                    case ArmourType.Metal: { desc = ("Metal"); break; }
                    case ArmourType.NonMetal: { desc = ("Non metal"); break; }
                    case ArmourType.Tabard: { desc = ("Tabard"); break; }
                    case ArmourType.Headwear: { desc = ("Headwear"); break; }
                }

                return desc;
            }
        }
    }

    /// <summary>
    /// An item quality.
    /// </summary>
    public class QualityItem
    {
        public BonusTypes Type { get; set; }
        public int Id { get; set; }
        public int Quantity { get; set; }
        public string Value { get; set; }
        public string Description { get; set; }
        public string ParentDescription { get; set; }
    }

    /// <summary>
    /// An armour type and description.
    /// </summary>
    public class ArmourSelectType
    {
        public ArmourType Type { get; set; }
        public string Description { get; set; }
    }

    /*
     *  Bonuses
     */

    /// <summary>
    /// Adds descriptive text to a bonus type 
    /// </summary>
    public class Quality
    {
        public BonusTypes Type { get; set; }
        public string Description { get; set; }
        public string Format { get; set; }
        public QualityChoiceType Choice { get; set; }
    }

    public enum QualityChoiceType
    {
        None,
        SkillName,
        SpellLevel,
        School,
        Sect,
        Quantity
    }

    /// <summary>
    /// A skill item for a select skill drop down list.
    /// </summary>
    public class QualitySkillItem
    {
        public string Name { get; set; }
        public string Slug { get; set; }
    }

    /// <summary>
    /// A sect select list item model.
    /// </summary>
    public class QualitySectItem
    {
        public string Name { get; set; }
        public string Slug { get; set; }
    }

    /// <summary>
    /// A school select list item model.
    /// </summary>
    public class QualitySchoolItem
    {
        public string Name { get; set; }
        public string Slug { get; set; }
    }

    /*
     *  Icon picker control models.
     */
    public class IconItem
    {
        public string File { get; set; }
        public string RelativePath { get; set; }
    }

    /*
     *  Item and ability model items
     */
    public abstract class ItemBase : IQualityContainer
    {
        public HQPad.Helpers.ItemType Type { get; set; }
        public string Name { get; set; }
        public int Id { get; set; }
        public string HQId { get; set; }
        public string Description { get; set; }
        public IconItem Icon { get; set; }
        public bool Equipped { get; set; }

        public string ItemName
        {
            get { return Name; }
        }

        protected List<QualityItem> qualities = null;

        public List<QualityItem> Qualities
        {
            get { return qualities; }
        }

        public ItemBase ()
        {
            this.qualities = new List<QualityItem>();
            Type = ItemType.Item;
            Icon = new IconItem()
            {
                File = "blue-gem.png",
                RelativePath = "/Content/Images/icons/equipment/blue-gem.png"
            };
        }
    }

    public class Item : ItemBase
    {
        public ItemUseType Use { get; set; }
        public string Frequency { get; set; }
        public int Charges { get; set; }

        public Item()
        {
            Use = ItemUseType.OnceEver;
            Type = ItemType.Item;
            Charges = 0;
        }
    }


    public class Scroll : ItemBase
    {
        public bool Written { get; set; }
        public string ContentSlug { get; set; }

        public Scroll()
        {
            Type = ItemType.Scroll;
        }
    }


    public class Ability : ItemBase
    {
        public ItemUseType Use { get; set; }
        public string Frequency { get; set; }
        public string Charges { get; set; }

        public Ability()
        {
            Type = ItemType.Ability;
        }
    }

    public class Potion : ItemBase
    {
        public int Doses { get; set; }

        public Potion()
        {
            Type = ItemType.Potion;
            Doses = 1;
        }
    }

    /// <summary>
    /// An equipment use type and description.
    /// </summary>
    public class EquipmentUseType
    {
        public ItemUseType Type { get; set; }
        public string Description { get; set; }
    }

    public class BattleboardPageModel
    {
        public string CharacterName { get; set; }
        public bool HasTalisman { get; set; }
        public int TotalPower { get; set; }
    }
}